home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / man / dev / pdev.man < prev    next >
Encoding:
Text File  |  1990-01-18  |  21.6 KB  |  545 lines

  1. .\"     $Header: /sprite/src/man/dev/RCS/pdev.man,v 1.3 90/01/17 18:05:14 jhh Exp $ SPRITE (Berkeley)
  2. .so \*(]ltmac.sprite
  3. .HS PDEV dev
  4. .BS
  5. .SH NAME
  6. Pseudo-devices \- files controlled by server processes.
  7. .BE
  8. .SH INTRODUCTION
  9. A pseudo-device is a special file that is controlled by a user-level
  10. process, which is called its \fIserver\fP.
  11. To all other processes, called \fIclients\fP, the pseudo-device is
  12. accessed like an ordinary file or device using regular Sprite
  13. system calls.
  14. This allows user-level processes to emulate a file or device with
  15. arbitrary characteristics.
  16. Pseudo-devices are used in Sprite for terminal emulation,
  17. access to Internet protocols, the stream communication
  18. used by the window system, and for the user-level implementation
  19. of other services.
  20. .PP
  21. This document describes how to write server programs 
  22. that control pseudo-devices using the raw kernel interface.
  23. There is also a Pdev library package that takes
  24. care of most of these details.
  25. See the Pdev man page for details.
  26. .PP
  27. A pseudo-device server is much like an RPC server;
  28. it waits for requests,
  29. performs some task,
  30. and returns results.
  31. The server has a \fIservice stream\fP for each client that has
  32. opened the pseudo-device.
  33. Each time the client makes an operation on the pseudo-device
  34. the kernel maps this into a request-response exchange over the
  35. service stream.
  36. The remaining sections describe this protocol in more detail.
  37. (The header file /sprite/lib/include/dev/pdev.h contains the
  38. type definitions repeated here, and describes the Fs_IOControl()
  39. calls mentioned here in more detail.)
  40. .SH "CONTROL STREAM"
  41. .PP
  42. The server of a pseudo-device is established by opening the
  43. pseudo-device with the O_MASTER flag.  This returns a
  44. \fIcontrol stream\fP to the server process.
  45. The server listens on the control stream for messages issued
  46. when client open the pseudo-device.
  47. .nf
  48.  
  49.   #include <sys/file.h>
  50.   #include <dev/pdev.h>
  51.  
  52.   cntrlStreamID = open("\fIpseudo-device\fP", O_MASTER | O_RDONLY, 0666); 
  53.  
  54. .fi
  55. The server's open call will fail with the FS_FILE_BUSY status if there is
  56. already a server process controlling the pseudo device.
  57. Similarly, a client's open will fail with DEV_OFFLINE if there
  58. is no server process controlling the pseudo-device.
  59. .PP
  60. The server establishes contact with a client in a two part process.
  61. First, it reads a Pdev_Notify message off the control stream
  62. that indicates the streamID of the
  63. new service stream used to communicate with the client.
  64. In the second phase,
  65. the server responds to an initial PDEV_OPEN
  66. request on the new service stream.
  67. The server's response determines if the client's
  68. open operation will succeed or fail.
  69. Refer to the following section
  70. on the request-response protocol for examples that use the control
  71. stream and handle client requests.
  72.  
  73. .SH "REQUEST-RESPONSE"
  74. .PP
  75. Whenever a client invokes an operation on the pseudo-device
  76. (Fs_Read, Fs_Write, Fs_IOControl, Fs_Close)
  77. the kernel forwards it
  78. to the server process so the server can implement it in any way it chooses.
  79. This is done using a request-response protocol much like
  80. a Remote Procedure Call (RPC).
  81. The kernel packages up the parameters of the system call,
  82. passes them to the server process in a \fIrequest message\fP,
  83. blocks the client process until a \fIreply message\fP is returned,
  84. and unpackages the return parameters from the reply message.
  85. This is transparent to the client, but not to the server.
  86. .PP
  87. This is the format of the request and reply messages:
  88. .PP
  89. .ta 3.0i
  90. .nf
  91.  
  92. typedef struct {
  93.     unsigned int magic;    /* PDEV_REQUEST_MAGIC or PFS_REQUEST_MAGIC */
  94.     int operation;    /* What action is requested. */
  95.     int messageSize;    /* The complete size of the request header
  96.      * plus data, plus padding for alignment */
  97.     int requestSize;    /* Size of data following this header */
  98.     int replySize;    /* Max size of the reply data expected. */
  99.     int dataOffset;    /* Offset of data from start of header */
  100. } Pdev_RequestHdr;
  101.  
  102. typedef struct {
  103.     Pdev_RequestHdr hdr;    /* with PDEV_REQUEST_MAGIC */
  104.     union {    /* Additional parameters to the operation. */
  105.         Pdev_OpenParam open;
  106.         Pdev_RWParam read;
  107.         Pdev_RWParam write;
  108.         Pdev_IOCParam ioctl;
  109.         Pdev_SetAttrParam setAttr;
  110.     } param;
  111.     /*
  112.      * Data, if any, follows.
  113.      */
  114. } Pdev_Request;
  115.  
  116. typedef struct Pdev_Reply {
  117.     unsigned int magic;    /* == PDEV_REPLY_MAGIC */
  118.     int status;    /* Return status of remote call */
  119.     int selectBits;    /* Return select state bits */
  120.     int replySize;    /* Size of the data in replyBuf, if any */
  121.     Address replyBuf;    /* Server space address of reply data */
  122.     int reserved;    /* Room for future expansion */
  123. } Pdev_Reply;
  124.  
  125. .fi
  126. .PP
  127. The server does not read the request messages directly from the service stream.
  128. Instead, there is a \fIrequest buffer\fP associated with each service stream
  129. that is in the server's own address space.
  130. The kernel puts request messages directly into this buffer.
  131. Access to the buffer is synchronized using two pointers,
  132. \fIfirstByte\fP and \fIlastByte\fP.  The server reads the
  133. values of these pointers from the service stream,
  134. and can safely examine the request(s) found in the request buffer
  135. between firstByte and lastByte.
  136. When the server is done with requests it updates firstByte by
  137. making an Fs_IOControl() call (IOC_PDEV_SET_PTRS) on the request stream.
  138. .PP
  139. The kernel fills the request buffer circularly, and it is possible that
  140. more than one request will be found between firstByte and lastByte.
  141. This occurs if write-behind is enabled (see below),
  142. or if the client process forks and both processes use their
  143. duplicated stream to the pseudo-device.
  144. As a convenience to servers, the kernel never wraps a request message
  145. around the end of the request buffer.
  146. Instead, if the request buffer fills up the kernel waits until the
  147. server has processed all the request messages before resetting and
  148. adding messages starting at the beginning of the buffer.
  149. .PP
  150. Three example procedures follow.
  151. The first, GetClient(),
  152. reads the control stream and sets up the new request stream
  153. and its associated request buffer.
  154. The second, Serve(), illustrates the use of \fIfirstByte\fP and \fIlastByte\fP.
  155. The last one, Reply(), uses Fs_IOControl to return the reply message.
  156. Fuller examples can be found in the Pdev library code,
  157. see /sprite/src/lib/c/etc/pdev.c.
  158. .nf
  159. .sp 1
  160. \fI/*
  161. \ * GetClient returns the streamID for a new request stream.
  162. \ */\fP
  163. int
  164. GetClient(cntrlStreamID, reqBufSize)
  165.     int cntrlStreamID;
  166.     int reqBufSize;
  167. {
  168.     Pdev_SetBufArgs setBuf;
  169.     Pdev_Notify notify;
  170.     int amountRead;
  171.     int newStreamID;
  172.  
  173.     \fI/*
  174.     \ * Read the control stream to get a new request stream.
  175.     \ * (You should check the return from Fs_Read and verify
  176.     \ *  the magic number in the Pdev_Notify structure.)
  177.     \ */\fP
  178.     Fs_Read(cntrlStreamID, sizeof(Pdev_Notify), (Address) ¬ify, &amountRead);
  179.     newStreamID = notify.streamID;
  180.     \fI/*
  181.     \ * Allocate the request buffer, and tell the kernel about it.
  182.     \ */\fP
  183.     setBuf.requestBufAddr = Mem_Alloc(reqBufSize);
  184.     setBuf.requestBufSize = reqBufSize;
  185.     setBuf.readBufAddr = 0;
  186.     setBuf.readBufSize = 0;
  187.     Fs_IOControl(newStreamID, IOC_PDEV_SET_BUF, sizeof(Pdev_SetBufArgs),
  188.             (Address)&setBuf, 0, 0);
  189.     return(newStreamID);
  190. }
  191. .fi
  192. .sp 1
  193. .nf
  194. Serve(requestStream, requestBuffer)
  195.     int requestStream;
  196.     Address requestBuffer;
  197. {
  198.     Pdev_BufPtrs bufPtrs;
  199.     int amountRead;
  200.     Pdev_Request *requestMsg;
  201.  
  202.     \fI/*
  203.     \ * Read the firstByte and lastByte pointers.
  204.     \ * (You should check the return from Fs_Read and verify
  205.     \ *  the magic number in the Pdev_BufPtrs structure.)
  206.     \ */\fP
  207.     Fs_Read(requestStreamID, sizeof(Pdev_BufPtrs), &bufPtrs, &amountRead);
  208.     while (bufPtrs.requestFirstByte < bufPtrs.requestLastByte) {
  209.             \fI/*
  210.             \ * Cast a pointer to the request message.
  211.             \ * (You should verify the magic number in the Pdev_Request.)
  212.             \ */\fP
  213.             requestMsg = (Pdev_Request *)&requestBuffer[bufPtrs.requestFirstByte];
  214.             switch (requestMsg->hdr.operation) {
  215.             \fI/*
  216.             \ * Switch out to operation specific code here...
  217.             \ */\fP
  218.             }
  219.             bufPtrs.requestFirstByte += requestMsg->hdr.messageSize;
  220.     }
  221.     \fI/*
  222.     \ * Move the firstByte pointer past the processed request messages.
  223.     \ */\fP
  224.     Fs_IOControl(requestStreamID, IOC_PDEV_SET_PTRS, sizeof(Pdev_BufPtrs), &bufPtrs, 0, 0);
  225. }
  226. .fi
  227. .sp 1
  228. .nf
  229. Reply(requestStream, status, selectBits, replyBuf)
  230.     int requestStream;
  231.     ReturnStatus status;
  232.     Address replyBuf;
  233. {
  234.     Pdev_Reply reply;
  235.     \fI/*
  236.     \ * Format and return the reply message.
  237.     \ */\fP
  238.     reply.magic = PDEV_REPLY_MAGIC;
  239.     reply.status = status;
  240.     reply.selectBits = selectBits;
  241.     reply.replySize = replySize;
  242.     reply.replyBuf = replyBuf;
  243.     Fs_IOControl(requestStream, IOC_PDEV_REPLY, sizeof(Pdev_Reply), (Address)&reply, 0, 0);
  244. }
  245. .fi
  246. .PP
  247. Let's review before moving on to select, write-behind, and read buffering.
  248. The control stream is returned when the server opens the pseudo-device
  249. using the O_MASTER flag.
  250. A service stream is created each time a client process opens the
  251. pseudo-device, and it is used by the server to handle requests
  252. from that client.
  253. The server reads the control stream to get new service stream IDs.
  254. The kernel forwards client operations on the pseudo-device to the
  255. server using a request-response protocol.
  256. The protocol uses a request buffer in the server's address space,
  257. and an associated pair of pointers, firstByte and lastByte.
  258. There is one request buffer and pair of pointers per service stream.
  259. The server reads new values of firstByte and lastByte from the
  260. service stream.
  261. After it is done with the request(s) found in the request buffer
  262. the server updates firstByte using IOC_PDEV_SET_PTRS.
  263. Replies are returned with another Fs_IOControl(), IOC_PDEV_REPLY.
  264. .SH "SELECT AND ASYNCHRONOUS I/O"
  265. .PP
  266. Note that the select operation is not forwarded to the server.
  267. It is too costly to switch out to the server process each time
  268. a client process makes a select that includes a stream to
  269. a pseudo-device.  Instead, the kernel maintains some select bits
  270. for each request stream
  271. (one bit each for readability, writability, and exceptional conditions)
  272. and checks this state itself.  The server updates the state bits each
  273. time it replies, or by making by using IOC_PDEV_READY.
  274. .PP
  275. The sever can optimize writes to the pseudo-device by enabling write-behind.
  276. With write-behind enabled the kernel does not block the client waiting
  277. for a reply to a write request.
  278. Instead, the write is assumed to have succeeded,
  279. and the client can continue to write until the request buffer fills up.
  280. This reduces the number of context switches made when handling writes
  281. to be proportional to the amount of data written,
  282. instead of proportional to the number of write calls by the client.
  283. If write-behind is enabled the operation code for writes will
  284. be \fBPDEV_WRITE_ASYNC\fP instead of \fBPDEV_WRITE\fP.
  285. Note that the server has to accept all data written (there is no
  286. opportunity for an error return),
  287. and it does not return a reply to write requests.
  288. Write-behind is enabled using the IOC_PDEV_WRITE_BEHIND Fs_IOControl() call.
  289. The input buffer for this Fs_IOControl() should contain a boolean which
  290. indicates whether or not write behind is enabled.
  291. .PP
  292. The server can optimize reads from the pseudo-device by using a
  293. read buffer.  The read buffer is used in a similar
  294. way as the request buffer.  In this case the server process adds
  295. data to the read buffer as it becomes available,
  296. and the kernel removes data in response to client read requests.
  297. Again, this reduces the number of context switches to the server process.
  298. Like the request buffer, the read buffer is in the server's address space.
  299. The IOC_PDEV_SET_BUF iocontrol() call is used to declare both buffers.
  300. Its input buffer contains a Pdev_SetBufArgs structure.
  301. Synchronization over the read buffer is also done with \fIfirstByte\fP
  302. and \fIlastByte\fP pointers.
  303. (The Pdev_BufPtrs structure that is read from the service stream contains
  304. a firstByte-lastByte pair for both the request and read buffers.)
  305. The kernel updates readFirstByte as the client process reads data,
  306. and the server process updates readLastByte as its adds data.
  307. The IOC_PDEV_SET_PTR iocontrol() call is used by the server to
  308. set both readLastByte and requestFirstByte.
  309. An important convention is that -1 (minus one) means ``no change''
  310. and can safely be passed in for either readLastByte or requestFirstByte.
  311. Another important convention is that the server should reset and
  312. begin filling the read buffer from the beginning after it empties.
  313. The server knows when it is empty when it reads (-1,-1) for readFirstByte
  314. and readLastByte.
  315. .SH "REGULAR OPERATIONS"
  316. .PP
  317. The following short sections describes the different request
  318. messages that the server will receive.
  319. They each have some extra parameters,
  320. and may require special actions on the part of the server.
  321. .IP PDEV_OPEN
  322. .ta 3.0i
  323. .nf
  324.  
  325.   typedef struct Pdev_OpenParam {
  326.     int flags;    /* Flags from the Fs_Open call */
  327.     Proc_PID pid;    /* Client's process ID */
  328.     int hostID;    /* Host ID where client is from */
  329.     int uid;    /* User ID of the client process */
  330.     Fmt_Format format;    /* Defines byte order of client machine */
  331.     int reserved;    /* Reserved for future expansion */
  332.   } Pdev_OpenParam;
  333.  
  334. .fi
  335. This is the first request to arrive on a new service stream when
  336. a client opens the pseudo-device.
  337. (The kernel waits until the request buffer is declared, of course,
  338. before issuing this request.)
  339. The client's open
  340. call will block until the server responds to this request.
  341. The reply status returned by the server will be the return status of the
  342. Fs_Open call by the client.
  343. The request parameters include a process ID and a user ID of
  344. the client so the server can do authentication.
  345. The format parameter is used in conjuction with library routines
  346. to handle byte swapping of data blocks sent and received with
  347. the PDEV_IOCTL command.
  348.  
  349. .IP PDEV_CLOSE
  350. A client has closed the device. This is the last message that will
  351. arrive on the service stream so the server should close the service stream.
  352. There are no close specific parameters in the request header.
  353.  
  354. .IP PDEV_READ
  355. .nf
  356.  
  357.   typedef struct {
  358.     int offset;    /* Read/Write byte offset */
  359.     unsigned int familyID;    /* Process group ID */
  360.     Proc_PID procID;    /* Process ID */
  361.     int reserved;    /* Extra */
  362.   } Pdev_RWParam;
  363.  
  364. .fi
  365. A client is requesting request.replySize bytes of data from the pseudo-device.
  366. The read request parameters include a byte offset at which the read should
  367. take place,
  368. and the process's familyID which can be used to enforce the notion
  369. of a controlling process group for the pseudo-device.
  370. The amount of data actually returned should be set in reply.replySize,
  371. and the status of the read should be set in reply.status.
  372. An end-of-file on the pseudo-device is indicated by returning zero bytes
  373. and a SUCCESS status.
  374. If no data is available the server should return
  375. the FS_WOULD_BLOCK status.  In this case the kernel will block
  376. the client process until the server indicates the pseudo-device is readable
  377. by making the IOC_PDEV_READY iocontrol() call:
  378. .nf
  379.  
  380.     bits = FS_WRITABLE | FS_READABLE;    \fI/* as appropriate... */\fP
  381.     status = Fs_IOControl(streamID, IOC_PDEV_READY, sizeof(int),
  382.         (Address)&bits, 0, 0;
  383.  
  384. .fi
  385. This will unblock the client process and cause another FS_PDEV_READ
  386. request to arrive on the service stream.
  387. .sp
  388. Note that the server will not see PDEV_READ requests if it has enabled
  389. read-ahead.  Read-ahead is implicitly enabled if the IOC_PDEV_SET_BUF
  390. iocontrol() call indicates a non-zero sized read-ahead buffer.
  391.  
  392. .IP PDEV_WRITE
  393. A client is writing data to the pseudo-device.
  394. The amount of data being written is indicated in request.requestSize,
  395. and the write parameters are the same as those for read:
  396. they include an offset, and a familyID, and a processID.
  397. The data written follows the request header immediately.
  398. The reply information on a synchronous write is an integer
  399. containing the number of bytes accepted by the server.
  400. The server can accept some (or none) of the data being written
  401. by returning FS_WOULD_BLOCK and the number of bytes accepted.
  402. The server unblocks the client process (as described above for FS_PDEV_READ)
  403. using the IOC_PDEV_READY iocontrol().
  404. .IP
  405. The semantics of returning FS_WOULD_BLOCK are important because the
  406. Sprite kernel takes care of blocking client processes and retrying
  407. write operations until the full amount of data is transferred to the
  408. pseudo-device, unless the stream is set to non-blocking, of course.
  409. To repeat, the write service routine should return FS_WOULD_BLOCK if
  410. it doesn't accept all the data given to it.
  411.  
  412. .IP PDEV_WRITE_SYNC
  413. If write-behind is enabled then this operation code is issued instead
  414. of \fBPDEV_WRITE\fP.  The message format is the same for both
  415. synchronous and asynchronous writes.
  416. The important difference is that the server has to
  417. accept all of the data and it does not return a reply.
  418. Thus asynchronous writes implicitly succeed.
  419.  
  420. .IP PDEV_IOCONTROL
  421. .nf
  422.  
  423.     typedef struct {
  424.     int command;    /* iocontrol() command #. */
  425.     unsigned int familyID;    /* Can be used to enforce controlling tty */
  426.     Proc_PID procID;    /* Process ID of client */
  427.     int byteOrder;    /* Defines client's byte ordering */
  428.     int reserved;    /* Extra */
  429.     } Pdev_IOCParam;
  430.  
  431. .fi
  432. A client is doing some device-specific operation.
  433. The Fs_IOControl parameters include the client's command,
  434. an inBuffer, and an outBuffer.
  435. The server process is free to define and implement any command
  436. it desires.  For example, the Internet protocol pseudo-devices supports
  437. commands to bind to addresses, accept connections, etc.
  438. .SH "BYTE ORDERING ISSUES"
  439. .PP
  440. In order to correctly support clients executing on machines with a
  441. different byte order than their server,
  442. the format field is used to define the client's byte order.
  443. A set of library routines is available to byte swap the
  444. block of data that follows the PDEV_IOCTL message header.
  445. It is the servers responsibility to byte swap the input data block
  446. of the PDEV_IOCTL command, and to byte swap the return data block.
  447. This is not an issue with reads and writes because the data
  448. is assumed to be a string of characters.
  449.  
  450. .SH "PSEUDO-FILE-SYSTEM SUPPORT"
  451. .PP
  452. Pseudo-device connections can be made into pseudo-file-systems when
  453. files in the pseudo-file-system are opened.
  454. The pseudo-device connection is exactly as described in this manual,
  455. except that the connection is created differently using
  456. the \fBIOC_PFS_OPEN\fP command on the pseudo-file-system naming
  457. service stream.  (See the devices pfs man page.)
  458. Additionally, however, there are two operations concerning attributes
  459. that appear in the request stream.
  460. These operations are only made on pseudo-device connections
  461. to pseudo-file-system servers.
  462.  
  463. .IP PDEV_GET_ATTR
  464. This is used to get the attributes of a file in a pseudo-file-system.
  465. There are no extra input parameters to this call.
  466. The returned data is an \fBFs_Attributes\fR record.
  467.  
  468. .IP PDEV_SET_ATTR
  469. .DS
  470. typedef struct {
  471.     int flags;    /* Which attributes to set */
  472.     int uid;    /* User ID */
  473.     int gid;    /* Group ID */
  474. } Pdev_SetAttrParam;
  475. .DE
  476. .PP
  477. This is used to set certain attributes of a file in a pseudo-file-system.
  478. The \fBflags\fP parameter is a combination of
  479. \fBFS_SET_TIMES\fR, \fBFS_SET_MODE\fR, \fBFS_SET_OWNER\fR,
  480. \fBFS_SET_FILE_TYPE\fR, \fBFS_SET_DEVICE\fR
  481. that indicates what attributes to set.
  482. The \fBuid\fP and \fBgid\fP identify the calling process and should be
  483. used to check permissions.  A \fBFs_Attributes\fP record follows the
  484. request message header and contains the new attributes.
  485. There is no return data for this call.
  486.  
  487. .SH "SERVER I/O CONTROLS"
  488. .PP
  489. The server uses a number of IOC_PDEV iocontrol() calls to implement
  490. its part of the request-response protocol.  These are summarized here,
  491. although the header file pdev.h can also be consulted.
  492. .IP IOC_PDEV_SET_BUF
  493. This is used to tell the kernel where the
  494. request buffer and read ahead buffer (if any)
  495. are. The input buffer should contain a
  496. Pdev_SetBufArgs struct.
  497. .IP IOC_PDEV_WRITE_BEHIND
  498. Set (Unset) write-behind buffering in the
  499. request buffer.  The single input argument
  500. is a pointer to a Boolean; TRUE enables
  501. write-behind, FALSE inhibits it.  The default
  502. is no write-behind.
  503. .IP IOC_PDEV_BIG_WRITES
  504. Set (Unset) the ability of the client to
  505. write a chunk larger than will fit into
  506. the request buffer.  This is to support
  507. UDP socket semantics that prevent a client
  508. from writing more than the declared packet size.
  509. The input buffer should reference a Boolean;
  510. TRUE enables big writes (which is the default)
  511. FALSE prevents big writes.
  512. The default is to allow big writes.  Large client write requests
  513. are broken up by the server into write requests that will
  514. fit into the request buffer.
  515. The request stream is locked during this so that no other
  516. client operations can slip in.
  517. .IP IOC_PDEV_SET_PTRS
  518. This is used to update the firstByte and
  519. lastByte pointers into the request and
  520. read ahead buffers.  The input buffer
  521. is a Pdev_BufPtrs structure.
  522. .IP IOC_PDEV_REPLY
  523. This is used to send a reply to a request.
  524. The input buffer contains a Pdev_Reply.  This
  525. includes an address (in the server's space)
  526. of a buffer containing reply data, if any.
  527. .IP IOC_PDEV_READY
  528. The server uses this to notify the kernel that the pseudo-device
  529. is ready for I/O now.
  530. The input buffer should contain an int with an or'd combination of
  531. FS_READABLE, FS_WRITABLE, or FS_EXCEPTION.
  532. .IP IOC_PDEV_SIGNAL_OWNER
  533. The server uses this to signal the owning process or process group.
  534. The input buffer is a \fBPdev_Signal\fR record containing a
  535. \fBsignal\fR and \fBcode\fR field.  The owner gets established by
  536. a IOC_SET_OWNER operation on the client end of the pseudo-device connection.
  537.  
  538. .SH FILES
  539. /sprite/lib/include/dev/pdev.h    \-    pseudo-device definitions
  540. .SH SEE ALSO
  541. Pdev, pfs, Fs_Open, Fs_Close, Fs_Read, Fs_Write, Fs_Select, Fs_IOControl,
  542. Fs_EventHandlerCreate, Bit
  543. .SH KEYWORDS
  544. pseudo-device, device, server, client, read, write, iocontrol
  545.